package com.java.oop;

/**
 * 单例模式: 通过对类进行设计,保证内存中类实例只有一个.
 * 具体方案通常是从两个维度进行设计:
 * 1.从类自身的维度进行设计(构造方法私有化,在类内部构建对象,为外部提供使用)
 * 1) 饿汉式单例 (类加载时创建类的实例,优点是设计简单并且线程安全,缺点是假如对象比较大对资源浪费就会比较严重)
 * 2) 懒汉式单例
 * 2.从外部维度进行设计(例如Spring,通过池化方式进行设计)
 */
//1.饿汉式单例-方案1
class Singleton01{//应用场景:小对象,频繁用(线程安全的),例如Java中的Runtime类
    private Singleton01(){}
    private static final Singleton01 instance=new Singleton01();
    public static Singleton01 getInstance() {
        return instance;
    }
}

//2.饿汉单例-方案2
enum Singleton02{
     INSTANCE;//类加载时创建对象实例,默认使用 public static final修饰
    // private Singleton02(){};//默认
}

//3.线程不安全的懒汉式单例
class Singleton03{
     private static Singleton03 instance;
     private Singleton03(){}
     public static Singleton03 getInstance() {
         if(instance==null){
             instance=new Singleton03();
         }
        return instance;
     }
}

//4.线程安全的懒汉式单例-方案1
class Singleton04{
    private static Singleton04 instance;
    private Singleton04(){}
    //线程安全,但是相对于饿汉的访问效率会比较低,原因时在这里会有线程阻塞和切换
    //适合场景:大对象,稀少用
    public static synchronized Singleton04 getInstance() {//锁-Singleton04.class(字节码对象)
        if(instance==null){
            instance=new Singleton04();
        }
        return instance;
    }
}

//5.线程安全的懒汉式单例-方案2(双重校验锁)
class Singleton05{
    //volatile的作用
    //1.禁止指令重排序
    //2.保证线程可见性(一个线程修改了这个变量的值,其它线程立刻可见)
    //3.不保证原子性(其原子性由synchronized关键字实现)
    private static volatile Singleton05 instance;
    private Singleton05(){}
    //减少阻塞,减少线程切换
    public static  Singleton05 getInstance() {//锁-Singleton04.class(字节码对象)
        if(instance==null) {
            synchronized (Singleton05.class) {
                if (instance == null) {
                    instance = new Singleton05();
                    //1.分配内存空间
                    //2.属性默认初始化
                    //3.调用构造方法
                    //4.将对象地址赋值给instance
                }
            }
        }
        return instance;
    }
}

public class SingletonTests {
    public static void main(String[] args) {

        for(int i=0;i<5;i++){
            new Thread(()->{
                System.out.println(Singleton03.getInstance());
            }).start();
        }
    }
}
